import time
from functools import wraps
import inspect
import datetime


def logger(fn1):
    print('@' * 30)

    @wraps(fn1)
    def wrapper1(*args, **kwargs):
        start = datetime.datetime.now()
        ret = fn1(*args, **kwargs)  # fn1 is wrapper2
        delta = (datetime.datetime.now() - start).total_seconds()
        print(fn1.__name__, delta)
        return ret

    return wrapper1


def mag_cache(fn2):
    print('#' * 30)
    local_cache = {}  # 每装饰一个函数,都有此函数的独立的cache@wraps(fn)

    @wraps(fn2)
    def wrapper2(*args, **kwargs):
        # makekey处理key
        target = {}  # 字典无序
        sig = inspect.signature(fn2)
        params = sig.parameters  # OrderedDict
        # 位置传参,变量名称的对应
        target.update(zip(params.keys(), args), **kwargs)  # 缺省值
        target.update(map(lambda k: (k, params[k].default), params.keys() - target.keys()))
        key = tuple(sorted(target.items()))
        if key not in local_cache.keys():
            local_cache[key] = fn2(*args, **kwargs)
        return key, local_cache[key]

    return wrapper2


@logger  # 2 add = logger(add) => add = logger (wrapper2) => add = wrapperl
@mag_cache  # 1 add = mag_cache(add) => add = wrapper2
# 如果有多个装饰器话，装饰过程，由近及远
def add(x=4, y=5):  # add = wrapper add = mag_cache(add)
    # add is wrapper1
    print('=' * 30)
    time.sleep(3)
    return x + y


print(1, add(4, 5))
print(2, add(y=5))
